var $ = (function () {

  function _ajax(opt) {
    // 兼容性创建xhr对象
    // 其中IE5/6是不支持XMLHttpRequest的
    // IE5/6/Opera Mini是只支持微软的ActiveXObject对象发送HTTP请求
    var xhr = window.XMLHttpRequest 
      ? new XMLHttpRequest() 
      : new ActiveXObject('Microsoft.XMLHTTP');

    if (!xhr) {
      // 如果无法创建xhr对象, 返回异常信息
      throw new Error('您的浏览器不支持异步发送HTTP请求');
    }

    // 从opt对象中获取AJAX配置信息
    var opt = opt || {},
      url = opt.url,
      method = (opt.method || 'GET').toUpperCase(),
      data = opt.data || null,
      // 请求成功的回调函数
      successCB = opt.successCB || function() {},
      // 无论请求是否成功都会执行的完成回调函数类似于finally
      completeCB = opt.completeCB || function() {},
      // 请求失败的回调函数
      errorCB = opt.errorCB || function() {},
      // 请求是否异步
      async = '' + opt.async === 'false' ? false : true,
      // 定时器保存id
      t = null,
      // 超时回调函数
      timeoutCB = opt.timeoutCB || function () {},
      // 超时时间
      timeout = opt.timeout || 30000    
    // 设置xhr对象的状态改变事件监听处理函数
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4) {
        // 当readyState处于状态4时，表示请求已完成、响应已就绪即未超时
        // 我们取消定时任务
        clearTimeout(t);        
        // 200-300之间都为请求成功的状态码, 304为浏览器使用缓存的重定向状态码
        if ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304) {          
          // 请求成功执行成功回调函数
          successCB(xhr.responseText);
        } else {
          // 请求失败执行失败回调函数
          errorCB(xhr.responseText);          
        }
        // 请求完成执行完成回调函数
        completeCB(xhr.responseText);
        t = null;
        xhr = null;
      } 
    }
    // Level2中的超时api
    // xhr.timeout = 500;
    // xhr.ontimeout = function() {
    //   console.log('ajax timeout');
    // }    

    // 建立客户端与服务器的连接、设置相应url、方法以及是否异步    
    xhr.open(method, url, async);

    // 当请求为POST请求时要设置客户端的Content-type以使服务器识别到请求是post请求的键值对形式
    method === 'POST' && xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

    // xhr对象发送请求，如果是GET请求不传入请求体、是POST请求则传入标准格式请求体
    // 如 name=zhangsan&&age=13
    xhr.send(method === 'GET' ? null : stringify(data));

    // 兼容性timeout
    // 发送请求后设置超时定时器，若在设定的超时时间内未清除定时器，则判断请求超时
    t = setTimeout(function() {
      timeoutCB();
      xhr.abort();
      // 以下是一种写法,可以省略
      clearTimeout(t);
      xhr = null;
      t = null;
      throw new Error('请求超时');
    }, timeout);
  }

  // 将对象转化为标准请求体格式
  function stringify(data) {
    var str = '';
    for (var key in data) {
      if (Object.prototype.hasOwnProperty.call(data, key)) {
        str += key + '=' + data[key] + '&';
      }
    }
    return str.replace(/&$/, '');
  }

  return {
    ajax: function(opt) {
      _ajax(opt);
    },
    get: function(url, successCB, errorCB) {
      _ajax({
        url: url,
        method: 'GET',
        successCB: successCB,        
        errorCB: errorCB
      });
    },
    post: function(url, data, successCB, errorCB) {
      _ajax({
        url: url,
        method: 'POST',
        data: data,
        successCB: successCB,
        errorCB: errorCB
      });
    }
  }
}());